package net.sareweb.acab.actions.generic;

import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.List;

import javax.faces.event.ActionEvent;
import javax.faces.model.DataModel;

import net.sareweb.acab.annotations.SearchComponent;
import net.sareweb.acab.entity.manager.CommonManager;
import net.sareweb.acab.exceptions.AcabException;

import org.jboss.seam.Component;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.Begin;
import org.jboss.seam.annotations.Create;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.contexts.Contexts;
import org.jboss.seam.international.StatusMessage;
import org.jboss.seam.international.StatusMessages;
import org.jboss.seam.jsf.ListDataModel;

@SuppressWarnings("serial")
@Scope(ScopeType.CONVERSATION)
public abstract class BasicBean<T> implements Serializable{

	private String selComponentName;
	private String managerComponentName;
	private String modelComponentName;
	private boolean creation;

	@In
	protected StatusMessages statusMessages;

	private CommonManager manager;

	private List<T> model;
	private T selected;

	public boolean isCreation() {
		return creation;
	}

	public void setCreation(boolean creation) {
		this.creation = creation;
	}

	public CommonManager getManager() {
		if(manager == null){
			manager = (CommonManager) Component.getInstance(getManagerComponentName());
		}
		return manager;
	}

	public void setManager(CommonManager manager) {
		this.manager = manager;
	}
	
	protected String getSelComponentName() {
		return selComponentName;
	}

	protected String getManagerComponentName() {
		return managerComponentName;
	}

	protected String getModelComponentName() {
		return modelComponentName;
	}

	public List<T> getModel() {
		return model;
	}

	public void setModel(List<T> model) {
		this.model = model;
		outject(getModelComponentName(), new ListDataModel(model));
	}
	
	protected T getSelected() {
		return selected;
	}

	private void setSelected(T selected){
		this.selected = selected;
		outject(getSelComponentName(), this.selected);
	}
	
	@SuppressWarnings("unchecked")
	public void onSelect(ActionEvent event){
		beforeSelect();
		DataModel model = (DataModel) Component.getInstance(getModelComponentName());
		setSelected((T) model.getRowData()); 
		afterSelect();
	}
	
	public void onCreate(ActionEvent event){
		try {
			setCreation(true);
			beforeCreate();
			setSelected(createNewInstance());
			afterCreate();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public void onRemoveSelected(ActionEvent event){
		onSelect(event);
		onRemoveCurrent(event);
	}
	
	public void onRemoveCurrent(ActionEvent event){
		beforeRemove();
		getManager().remove(selected);
		afterRemove();
		onSearch(event);
		setSelected(null);
		getManager().flush();
	}
	
	public void onSearch(ActionEvent event){
		//TODO de momento lo tengo en la clase hija
	}

	public void onSave(ActionEvent event){
		try{
			beforeSave();
			getManager().save(selected);
			setCreation(false);
			afterSave();
			getManager().flush();
		}catch(AcabException e){
			handleException(e);
		}
	}

	public void onCancel(ActionEvent event){
		getManager().discardChanges(getSelected());
		setSelected(null);
		setCreation(false);
		onSearch(event);
	}

	@Create
	@Begin
	public void initBean(){
		Annotation annotation = this.getClass().getAnnotation(SearchComponent.class);
		if(annotation != null){
			SearchComponent sAnn = (SearchComponent)annotation;
			this.managerComponentName = sAnn.manager();
			this.modelComponentName =  sAnn.model();
			this.selComponentName = sAnn.selection();
		}
		setModel(new ArrayList<T>());
	}

	protected void outject(String name, Object o){
		Contexts.getConversationContext().set(name, o);
	}

	protected void beforeSelect() {
		// A sobreescribir en las clases hijas
	}

	protected void afterSelect() {
		// A sobreescribir en las clases hijas
	}

	protected void afterCreate() {
		// A sobreescribir en las clases hijas
	}

	protected void beforeCreate() {
		// A sobreescribir en las clases hijas
	}

	protected boolean beforeSave() {
		// A sobreescribir en las clases hijas
		return true;
	}

	protected void afterSave() {
		// A sobreescribir en las clases hijas
	}

	protected void beforeRemove() {
		// A sobreescribir en las clases hijas
	}

	protected void afterRemove() {
		// A sobreescribir en las clases hijas
	}

	@SuppressWarnings("unchecked")
	private T createNewInstance() throws InstantiationException, IllegalAccessException {
		ParameterizedType genSupClass = (ParameterizedType) this.getClass().getGenericSuperclass();
		Class<T> clazz = (Class)genSupClass.getActualTypeArguments()[0];
		return clazz.newInstance();
	}
	
	private void handleException(AcabException e) {
		if(e.getComponentName() != null && !"".equals(e.getComponentName())){
			statusMessages.addToControl(e.getComponentName(), StatusMessage.Severity.ERROR, e.getMessage(), new Object[0]);
		}else{
			statusMessages.add(StatusMessage.Severity.ERROR, e.getMessage(), new Object[0]);
		}
	}
}
